from loguru import logger
import json
import re
from tools.tool_manager import ToolManager
import inspect

class ToolAgent:
    def __init__(self):
        self.tool_manager = ToolManager()
        self.settings = self._load_settings()
        self.default_userid = self.settings.get("userid", "test111")
        self.default_openid = self.settings.get("openid", "zyinfoai")
        logger.info("ToolAgent initialized with ToolManager.")

    def _load_settings(self):
        settings = {}
        try:
            with open("./settings.txt", 'r', encoding='utf-8') as f:
                settings = json.load(f)
        except FileNotFoundError:
            logger.error("Error: settings.txt not found. Cannot load default userid/openid.")
        except json.JSONDecodeError:
            logger.error("Error: Could not decode JSON from settings.txt. Please check the file format.")
        return settings

    def process_command(self, request_json: str) -> str:
        logger.info(f"ToolAgent received request: {request_json}")
        
        try:
            request = json.loads(request_json)
            task = request.get("task", {})
            tool_name = task.get("tool_name")
            params = task.get("params", {})
            # Attempt to parse params as XML if it's a string
            if isinstance(params, str):
                try:
                    import xml.etree.ElementTree as ET
                    root = ET.fromstring(params)
                    # If there's only one child and its tag matches a common single-param name (e.g., 'path', 'query', 'code')
                    # or if the tool expects a single string argument, extract its text.
                    # Otherwise, parse all children into a dictionary.
                    tool_func_signature = inspect.signature(self.tool_manager.get_tool_func(tool_name))
                    if len(root) == 1 and len(tool_func_signature.parameters) == 1:
                        # Assuming the single parameter is the text of the root's only child
                        params = root[0].text
                    else:
                        parsed_params = {}
                        for child in root:
                            parsed_params[child.tag] = child.text
                        params = parsed_params
                except ET.ParseError:
                    # If it's not valid XML, keep it as is (it might be a direct string value)
                    pass

            if not tool_name:
                return self._format_error_response(request.get("task_id"), "Missing 'tool_name' in request.")

            tool_func = self.tool_manager.get_tool_func(tool_name)
            if tool_func:
                context = task.get("context", {})
                userid = context.get("userid", self.default_userid) # Default userid
                openid = context.get("openid", self.default_openid) # Default openid

                # Prepare arguments for the tool function
                tool_args = {}

                sig = inspect.signature(tool_func)
                logger.debug(f"Tool arguments before calling tool_func: {tool_args!r}")
                logger.debug(f"Params received by ToolAgent: {params!r}")

                if tool_name == "execute_in_sandbox":
                    if isinstance(params, dict):
                        tool_args["code"] = params.get("code_to_execute", "")
                        tool_args["timeout"] = int(params.get("timeout", 60))
                    else:
                        # Fallback for unexpected params format, though it should be a dict now
                        logger.warning(f"execute_in_sandbox received unexpected params format: {params!r}")
                        tool_args["code"] = str(params) # Treat as code directly
                        tool_args["timeout"] = 60
                # Populate tool_args based on the tool's signature and provided params
                for param_name, param_obj in sig.parameters.items():
                    if param_name in ["userid", "openid"]:
                        continue # Handled separately below

                    if isinstance(params, dict) and param_name in params:
                        tool_args[param_name] = params[param_name]
                    elif not isinstance(params, dict) and param_obj.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD:
                        # If params is a single value and this is the first positional/keyword argument
                        if not tool_args: # Only assign if tool_args is still empty
                            tool_args[param_name] = params


                # Add userid and openid if the tool function accepts them
                if "userid" in sig.parameters:
                    tool_args["userid"] = userid
                if "openid" in sig.parameters:
                    tool_args["openid"] = openid

                try:
                    result = tool_func(**tool_args)
                except TypeError as te:
                    # This can happen if the tool function's signature doesn't match the passed arguments
                    logger.error(f"TypeError when calling tool {tool_name} with args {tool_args}: {te}", exc_info=True)
                    return self._format_error_response(request.get("task_id"), f"Tool execution error: Invalid arguments for tool {tool_name}. {te}")
                except Exception as e:
                    logger.error(f"Error during tool {tool_name} execution: {e}", exc_info=True)
                    return self._format_error_response(request.get("task_id"), f"Tool execution error: {e}")

                return self._format_success_response(request.get("task_id"), result)
            else:
                return self._format_error_response(request.get("task_id"), f"Tool '{tool_name}' not found in ToolManager.")
        except json.JSONDecodeError:
            return self._format_error_response(None, "Invalid JSON request.")
        except Exception as e:
            logger.error(f"Error executing tool command: {e}", exc_info=True)
            return self._format_error_response(None, f"Error executing tool command: {e}")

    def _format_success_response(self, task_id: str, result: any) -> str:
        response = {
            "protocol_version": "1.0",
            "task_id": task_id,
            "responder": "ToolAgent",
            "status": "success",
            "result": {
                "output": result,
                "error_message": ""
            }
        }
        return json.dumps(response)

    def _format_error_response(self, task_id: str, error_message: str) -> str:
        response = {
            "protocol_version": "1.0",
            "task_id": task_id,
            "responder": "ToolAgent",
            "status": "failure",
            "result": {
                "output": "",
                "error_message": error_message
            }
        }
        return json.dumps(response)